#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#define IP_ADDR "172.23.196.114"

int main()
{
    int sockfd = -1;
    //对于 IPV4 来讲绑定时用的结构体就是这个。
    struct sockaddr_in sin;

    // 1. 经典的创建 socket 函数调用方式。
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        exit(-1);
    }

    /*
        由于 bind() 函数中参数 addr 所代表的通用结构体 sockaddr 的长度与
        IPV4 下的 sockaddr_in 结构体不一致，因此需要将多余的数据部分清零。
    */
    bzero(&sin, sizeof(sin));

    sin.sin_family = AF_INET;   //对于 IPV4 来讲这个变量的值恒填 AF_INET。
    sin.sin_port = htons(5905);//端口号，要求要网络字节序，因此需要作转换。
    //sin.sin_addr.s_addr = inet_addr(IP_ADDR);//这种将字符串形式IPV4地址转换成整数形式
                                               //的函数比较落后且有缺陷，一般不建议使用。
    //可靠的 inet_pton() 转换函数
    if(inet_pton(AF_INET, IP_ADDR, (void *)&sin.sin_addr.s_addr) != 1) //inet_pton() 的返回值较特殊。
    {
        perror("inet_pton");
        exit(-1);
    }

    // 2. 绑定操作。
    if(bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("bind");
        exit(-1);
    }
    
    // 3. 开始监听
    if(listen(sockfd, 5) < 0)
    {
        perror("listen");
        exit(-1);
    }
    
    // 4. 等待连接
    int newfd = -1;
    newfd = accept(sockfd, NULL, NULL);
    if(newfd < 0)
    {
        perror("accept");
        exit(-1);
    }
    
    // 5. 读写
    int ret = -1;
    char buf[64];
    while(1)
    {
        bzero(buf, 64);
        do{
            ret = read(newfd, buf, 64 - 1);
        }while(ret < 0 && EINTR == errno);
        
        if(ret < 0)
        {
            perror("read");
            exit(-1);
        }
        
        if(ret == 0)
        {
            //客户端已经关闭连接。
        printf("Client was exited!\n");
            break;
        }
        
        printf("Received:%s\n", buf);
    }
    
    close(newfd);
    close(sockfd);

    printf("bye\n");

    return 0;
}